#include "lilypond_writer.h"

#include <QFile>
#include <QIODevice>
#include <QTextStream>

#include "core/music_constraints.h"
#include "core/music_settings.h"

LilyPondWriter::LilyPondWriter(MusicSettings *settings,
                               MusicConstraints *constraints):
                               settings(settings),
                               constraints(constraints) {
}

/*!
 * Écrit un fichier au format Lilypond en fonction de son utilisation
 * Certaines parties du fichier ne sont pas générées selon l'utilisation choisie
 *
 * Manuel de notation :
 * http://lilypond.org/doc/v2.12/Documentation/user/lilypond/index.html
 */
void LilyPondWriter::write(QString filename, Usage usage) {
  QFile file(filename);
  if (!file.open(QIODevice::WriteOnly))
    return;

  QTextStream out(&file);

  // header
  if(usage == INTERNAL || usage == PDF) {
    out << "\\header {\n"
           " tagline = ##f\n" // pas de ligne de tag à la fin de la partition
           "}\n"
           "\n";
  }

  // partition
  out << "\\score {\n"
         " {\n";

  // armure
  if(settings->scale != MusicSettings::UNDEFINED_SCALE &&
     settings->mode != MusicSettings::UNDEFINED_MODE) {
    out << "  "
           "\\key " << MusicSettings::scaleToString(settings->scale)
                    << " \\"
                    << MusicSettings::modeToString(settings->mode) << "\n";
  }

  // notes
  out << "  ";
  for(int i = 0; i < settings->scoreSize; i++) {
    int pitch = constraints->pitch[i].val(); // valeur de la note
    int octave = constraints->octave[i].val(); // numéro d'octave

    out << pitchToString(pitch, settings->scale, settings->mode);
    if (pitch != 0) // si ce n'est pas un silence
      for(int j = 0; j < octave - 1; j++)
        out << "'";
    out << "8 "; // croche
  }
  out << "\\bar \"|.\"\n"; // barre de fin de partition

  out << " }\n";

  // information pour le fichier MIDI
  if(usage == INTERNAL) {
    out << "\n"
           " \\midi {\n"
           "   \\context {\n"
           "    \\Score\n"
           "    tempoWholesPerMinute = #(ly:make-moment " << settings->tempo
                                                          << " 4)\n"
           "   }\n"
           " }\n";

    // layout
    out << "\n"
           " \\layout {\n"
           "  \\context {\n"
           "   \\Score\n"
           "    \\remove \"Bar_number_engraver\"\n" // pas de numéro de mesure
           "  }\n"
           " }\n";
  }

  out << "}\n"; // fin de partition

  // papier
  if(usage == INTERNAL) {
    out << "\n"
           "\\paper {\n"
           " line-width = 150\\mm\n" // taille des lignes
           " paper-height = 200\\mm\n" // hauteur de la page
           " print-page-number = ##f\n" // pas de numéro de page
           " ragged-bottom = ##t\n"
           "}\n";
  }
}

/*!
 * Convertit une note du CSP en chaîne de caractères en fonction du mode et de
 * la tonalité
 * Le format utilisé est celui de Lilypond
 *
 * Writing pitches :
 * http://lilypond.org/doc/v2.12/Documentation/user/lilypond/Writing-pitches.html
 */
QString LilyPondWriter::pitchToString(int pitch,
                                      MusicSettings::Scale scale,
                                      MusicSettings::Mode mode) {
  switch(pitch) {
    case 0: // silence
      return "r";
    case 1: // do
      switch(mode) {
        case MusicSettings::UNDEFINED_MODE:
          return "c'";
        case MusicSettings::MAJOR:
          switch(scale) {
            case MusicSettings::C:
            case MusicSettings::G:
            case MusicSettings::D:
            case MusicSettings::A:
            case MusicSettings::E:
            case MusicSettings::B:
            case MusicSettings::FIS:
              return "c'";
            case MusicSettings::CIS:
              return "bis";
            case MusicSettings::F:
            case MusicSettings::BES:
            case MusicSettings::EES:
            case MusicSettings::AES:
            case MusicSettings::DES:
            case MusicSettings::GES:
            case MusicSettings::CES:
              return "c'";
            default:
              return NULL;
          }
        case MusicSettings::MINOR:
          switch(scale) {
            case MusicSettings::A:
            case MusicSettings::E:
            case MusicSettings::B:
            case MusicSettings::FIS:
            case MusicSettings::CIS:
            case MusicSettings::GIS:
            case MusicSettings::DIS:
              return "c'";
            case MusicSettings::AIS:
              return "bis";
            case MusicSettings::D:
              return "cis'"; // sensible
            case MusicSettings::G:
            case MusicSettings::C:
            case MusicSettings::F:
            case MusicSettings::BES:
            case MusicSettings::EES:
            case MusicSettings::AES:
              return "c'";
            default:
              return NULL;
          }
      }
    case 2: // do dièse
      switch(mode) {
        case MusicSettings::UNDEFINED_MODE:
          return "cis'";
        case MusicSettings::MAJOR:
          switch(scale) {
            case MusicSettings::C:
            case MusicSettings::G:
            case MusicSettings::D:
            case MusicSettings::A:
            case MusicSettings::E:
            case MusicSettings::B:
            case MusicSettings::FIS:
            case MusicSettings::CIS:
              return "cis'";
            case MusicSettings::F:
            case MusicSettings::BES:
            case MusicSettings::EES:
            case MusicSettings::AES:
            case MusicSettings::DES:
            case MusicSettings::GES:
            case MusicSettings::CES:
              return "des'";
            default:
              return NULL;
          }
        case MusicSettings::MINOR:
          switch(scale) {
            case MusicSettings::A:
            case MusicSettings::E:
            case MusicSettings::B:
            case MusicSettings::FIS:
            case MusicSettings::CIS:
            case MusicSettings::GIS:
              return "cis'";
            case MusicSettings::DIS:
              return "cisis'"; // sensible
            case MusicSettings::AIS:
              return "cis'";
            case MusicSettings::D:
            case MusicSettings::G:
            case MusicSettings::C:
            case MusicSettings::F:
            case MusicSettings::BES:
              return "des'";
            case MusicSettings::EES:
              return "d'"; // sensible
            case MusicSettings::AES:
              return "des'";
            default:
              return NULL;
          }
      }
    case 3: // ré
      switch(mode) {
        case MusicSettings::UNDEFINED_MODE:
          return "d'";
        case MusicSettings::MAJOR:
          switch(scale) {
            case MusicSettings::C:
            case MusicSettings::G:
            case MusicSettings::D:
            case MusicSettings::A:
            case MusicSettings::E:
            case MusicSettings::B:
            case MusicSettings::FIS:
            case MusicSettings::CIS:
            case MusicSettings::F:
            case MusicSettings::BES:
            case MusicSettings::EES:
            case MusicSettings::AES:
            case MusicSettings::DES:
            case MusicSettings::GES:
            case MusicSettings::CES:
              return "d'";
            default:
              return NULL;
          }
        case MusicSettings::MINOR:
          switch(scale) {
            case MusicSettings::A:
              return "d'";
            case MusicSettings::E:
              return "dis'"; // sensible
            case MusicSettings::B:
            case MusicSettings::FIS:
            case MusicSettings::CIS:
            case MusicSettings::GIS:
            case MusicSettings::DIS:
            case MusicSettings::AIS:
            case MusicSettings::D:
            case MusicSettings::G:
            case MusicSettings::C:
            case MusicSettings::F:
            case MusicSettings::BES:
            case MusicSettings::EES:
            case MusicSettings::AES:
              return "d'";
            default:
              return NULL;
          }
      }
    case 4: // ré dièse
      switch(mode) {
        case MusicSettings::UNDEFINED_MODE:
          return "dis'";
        case MusicSettings::MAJOR:
          switch(scale) {
            case MusicSettings::C:
            case MusicSettings::G:
            case MusicSettings::D:
            case MusicSettings::A:
            case MusicSettings::E:
            case MusicSettings::B:
            case MusicSettings::FIS:
            case MusicSettings::CIS:
              return "dis'";
            case MusicSettings::F:
            case MusicSettings::BES:
            case MusicSettings::EES:
            case MusicSettings::AES:
            case MusicSettings::DES:
            case MusicSettings::GES:
            case MusicSettings::CES:
              return "ees'";
            default:
              return NULL;
          }
        case MusicSettings::MINOR:
          switch(scale) {
            case MusicSettings::A:
            case MusicSettings::E:
            case MusicSettings::B:
            case MusicSettings::FIS:
            case MusicSettings::CIS:
            case MusicSettings::GIS:
            case MusicSettings::DIS:
            case MusicSettings::AIS:
              return "dis'";
            case MusicSettings::D:
            case MusicSettings::G:
            case MusicSettings::C:
              return "ees'";
            case MusicSettings::F:
              return "e'"; // sensible
            case MusicSettings::BES:
            case MusicSettings::EES:
            case MusicSettings::AES:
              return "ees'";
            default:
              return NULL;
          }
      }
    case 5: // mi
      switch(mode) {
        case MusicSettings::UNDEFINED_MODE:
          return "e'";
        case MusicSettings::MAJOR:
          switch(scale) {
            case MusicSettings::C:
            case MusicSettings::G:
            case MusicSettings::D:
            case MusicSettings::A:
            case MusicSettings::E:
            case MusicSettings::B:
            case MusicSettings::FIS:
            case MusicSettings::CIS:
            case MusicSettings::F:
            case MusicSettings::BES:
            case MusicSettings::EES:
            case MusicSettings::AES:
            case MusicSettings::DES:
            case MusicSettings::GES:
              return "e'";
            case MusicSettings::CES:
              return "fes'";
            default:
              return NULL;
          }
        case MusicSettings::MINOR:
          switch(scale) {
            case MusicSettings::A:
            case MusicSettings::E:
            case MusicSettings::B:
              return "e'";
            case MusicSettings::FIS:
              return "eis'"; // sensible
            case MusicSettings::CIS:
            case MusicSettings::GIS:
            case MusicSettings::DIS:
            case MusicSettings::AIS:
            case MusicSettings::D:
            case MusicSettings::G:
            case MusicSettings::C:
            case MusicSettings::F:
            case MusicSettings::BES:
            case MusicSettings::EES:
              return "e'";
            case MusicSettings::AES:
              return "fes'";
            default:
              return NULL;
          }
      }
    case 6: // fa
      switch(mode) {
        case MusicSettings::UNDEFINED_MODE:
          return "f'";
        case MusicSettings::MAJOR:
          switch(scale) {
            case MusicSettings::C:
            case MusicSettings::G:
            case MusicSettings::D:
            case MusicSettings::A:
            case MusicSettings::E:
            case MusicSettings::B:
              return "f'";
            case MusicSettings::FIS:
            case MusicSettings::CIS:
              return "eis'";
            case MusicSettings::F:
            case MusicSettings::BES:
            case MusicSettings::EES:
            case MusicSettings::AES:
            case MusicSettings::DES:
            case MusicSettings::GES:
            case MusicSettings::CES:
              return "f'";
            default:
              return NULL;
          }
        case MusicSettings::MINOR:
          switch(scale) {
            case MusicSettings::A:
            case MusicSettings::E:
            case MusicSettings::B:
            case MusicSettings::FIS:
            case MusicSettings::CIS:
            case MusicSettings::GIS:
              return "f'";
            case MusicSettings::DIS:
            case MusicSettings::AIS:
              return "eis'";
            case MusicSettings::D:
              return "f'";
            case MusicSettings::G:
              return "fis'"; // sensible
            case MusicSettings::C:
            case MusicSettings::F:
            case MusicSettings::BES:
            case MusicSettings::EES:
            case MusicSettings::AES:
              return "f'";
            default:
              return NULL;
          }
      }
    case 7: // fa dièse
      switch(mode) {
        case MusicSettings::UNDEFINED_MODE:
          return "fis'";
        case MusicSettings::MAJOR:
          switch(scale) {
            case MusicSettings::C:
            case MusicSettings::G:
            case MusicSettings::D:
            case MusicSettings::A:
            case MusicSettings::E:
            case MusicSettings::B:
            case MusicSettings::FIS:
            case MusicSettings::CIS:
              return "fis'";
            case MusicSettings::F:
            case MusicSettings::BES:
            case MusicSettings::EES:
            case MusicSettings::AES:
            case MusicSettings::DES:
            case MusicSettings::GES:
            case MusicSettings::CES:
              return "ges'";
            default:
              return NULL;
          }
        case MusicSettings::MINOR:
          switch(scale) {
            case MusicSettings::A:
            case MusicSettings::E:
            case MusicSettings::B:
            case MusicSettings::FIS:
            case MusicSettings::CIS:
              return "fis'";
            case MusicSettings::GIS:
              return "fisis'"; // sensible
            case MusicSettings::DIS:
            case MusicSettings::AIS:
              return "fis'";
            case MusicSettings::D:
            case MusicSettings::G:
            case MusicSettings::C:
            case MusicSettings::F:
            case MusicSettings::BES:
            case MusicSettings::EES:
              return "ges'";
            case MusicSettings::AES:
              return "g'"; // sensible
            default:
              return NULL;
          }
      }
    case 8: // sol
      switch(mode) {
        case MusicSettings::UNDEFINED_MODE:
          return "g'";
        case MusicSettings::MAJOR:
          switch(scale) {
            case MusicSettings::C:
            case MusicSettings::G:
            case MusicSettings::D:
            case MusicSettings::A:
            case MusicSettings::E:
            case MusicSettings::B:
            case MusicSettings::FIS:
            case MusicSettings::CIS:
            case MusicSettings::F:
            case MusicSettings::BES:
            case MusicSettings::EES:
            case MusicSettings::AES:
            case MusicSettings::DES:
            case MusicSettings::GES:
            case MusicSettings::CES:
              return "g'";
            default:
              return NULL;
          }
        case MusicSettings::MINOR:
          switch(scale) {
            case MusicSettings::A:
              return "gis'"; // sensible
            case MusicSettings::E:
            case MusicSettings::B:
            case MusicSettings::FIS:
            case MusicSettings::CIS:
            case MusicSettings::GIS:
            case MusicSettings::DIS:
            case MusicSettings::AIS:
            case MusicSettings::D:
            case MusicSettings::G:
            case MusicSettings::C:
            case MusicSettings::F:
            case MusicSettings::BES:
            case MusicSettings::EES:
            case MusicSettings::AES:
              return "g'";
            default:
              return NULL;
          }
      }
    case 9: // sol dièse
      switch(mode) {
        case MusicSettings::UNDEFINED_MODE:
          return "gis'";
        case MusicSettings::MAJOR:
          switch(scale) {
            case MusicSettings::C:
            case MusicSettings::G:
            case MusicSettings::D:
            case MusicSettings::A:
            case MusicSettings::E:
            case MusicSettings::B:
            case MusicSettings::FIS:
            case MusicSettings::CIS:
              return "gis'";
            case MusicSettings::F:
            case MusicSettings::BES:
            case MusicSettings::EES:
            case MusicSettings::AES:
            case MusicSettings::DES:
            case MusicSettings::GES:
            case MusicSettings::CES:
              return "aes'";
            default:
              return NULL;
          }
        case MusicSettings::MINOR:
          switch(scale) {
            case MusicSettings::A:
            case MusicSettings::E:
            case MusicSettings::B:
            case MusicSettings::FIS:
            case MusicSettings::CIS:
            case MusicSettings::GIS:
            case MusicSettings::DIS:
              return "gis'";
            case MusicSettings::AIS:
              return "gisis'"; // sensible
            case MusicSettings::D:
            case MusicSettings::G:
            case MusicSettings::C:
            case MusicSettings::F:
              return "aes'";
            case MusicSettings::BES:
              return "a'"; // sensible
            case MusicSettings::EES:
            case MusicSettings::AES:
              return "aes'";
            default:
              return NULL;
          }
      }
    case 10: // la
      switch(mode) {
        case MusicSettings::UNDEFINED_MODE:
          return "a'";
        case MusicSettings::MAJOR:
          switch(scale) {
            case MusicSettings::C:
            case MusicSettings::G:
            case MusicSettings::D:
            case MusicSettings::A:
            case MusicSettings::E:
            case MusicSettings::B:
            case MusicSettings::FIS:
            case MusicSettings::CIS:
            case MusicSettings::F:
            case MusicSettings::BES:
            case MusicSettings::EES:
            case MusicSettings::AES:
            case MusicSettings::DES:
            case MusicSettings::GES:
            case MusicSettings::CES:
              return "a'";
            default:
              return NULL;
          }
        case MusicSettings::MINOR:
          switch(scale) {
            case MusicSettings::A:
            case MusicSettings::E:
              return "a'";
            case MusicSettings::B:
              return "ais'"; // sensible
            case MusicSettings::FIS:
            case MusicSettings::CIS:
            case MusicSettings::GIS:
            case MusicSettings::DIS:
            case MusicSettings::AIS:
            case MusicSettings::D:
            case MusicSettings::G:
            case MusicSettings::C:
            case MusicSettings::F:
            case MusicSettings::BES:
            case MusicSettings::EES:
            case MusicSettings::AES:
              return "a'";
            default:
              return NULL;
          }
      }
    case 11: // la dièse
      switch(mode) {
        case MusicSettings::UNDEFINED_MODE:
          return "ais'";
        case MusicSettings::MAJOR:
          switch(scale) {
            case MusicSettings::C:
            case MusicSettings::G:
            case MusicSettings::D:
            case MusicSettings::A:
            case MusicSettings::E:
            case MusicSettings::B:
            case MusicSettings::FIS:
            case MusicSettings::CIS:
              return "ais'";
            case MusicSettings::F:
            case MusicSettings::BES:
            case MusicSettings::EES:
            case MusicSettings::AES:
            case MusicSettings::DES:
            case MusicSettings::GES:
            case MusicSettings::CES:
              return "bes'";
            default:
              return NULL;
          }
        case MusicSettings::MINOR:
          switch(scale) {
            case MusicSettings::A:
            case MusicSettings::E:
            case MusicSettings::B:
            case MusicSettings::FIS:
            case MusicSettings::CIS:
            case MusicSettings::GIS:
            case MusicSettings::DIS:
            case MusicSettings::AIS:
              return "ais'";
            case MusicSettings::D:
            case MusicSettings::G:
              return "bes'";
            case MusicSettings::C:
              return "b'"; // sensible
            case MusicSettings::F:
            case MusicSettings::BES:
            case MusicSettings::EES:
            case MusicSettings::AES:
              return "bes'";
            default:
              return NULL;
          }
      }
    case 12: // si
      switch(mode) {
        case MusicSettings::UNDEFINED_MODE:
          return "b'";
        case MusicSettings::MAJOR:
          switch(scale) {
            case MusicSettings::C:
            case MusicSettings::G:
            case MusicSettings::D:
            case MusicSettings::A:
            case MusicSettings::E:
            case MusicSettings::B:
            case MusicSettings::FIS:
            case MusicSettings::CIS:
            case MusicSettings::F:
            case MusicSettings::BES:
            case MusicSettings::EES:
            case MusicSettings::AES:
            case MusicSettings::DES:
              return "b'";
            case MusicSettings::GES:
            case MusicSettings::CES:
              return "ces''";
            default:
              return NULL;
          }
        case MusicSettings::MINOR:
          switch(scale) {
            case MusicSettings::A:
            case MusicSettings::E:
            case MusicSettings::B:
            case MusicSettings::FIS:
              return "b'";
            case MusicSettings::CIS:
              return "bis'"; // sensible
            case MusicSettings::GIS:
            case MusicSettings::DIS:
            case MusicSettings::AIS:
            case MusicSettings::D:
            case MusicSettings::G:
            case MusicSettings::C:
            case MusicSettings::F:
            case MusicSettings::BES:
              return "b'";
            case MusicSettings::EES:
            case MusicSettings::AES:
              return "ces''";
            default:
              return NULL;
          }
      }
    default:
      return NULL;
  }
}
